package com.meida.module.arc.provider.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.meida.common.base.entity.EntityMap;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.mybatis.query.CriteriaDelete;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.mybatis.query.CriteriaSave;
import com.meida.common.mybatis.query.CriteriaUpdate;
import com.meida.common.utils.ApiAssert;
import com.meida.common.utils.RedisUtils;
import com.meida.common.utils.SpringContextHolder;
import com.meida.module.arc.client.constants.ArchiveConstants;
import com.meida.module.arc.client.entity.ArcCategory;
import com.meida.module.arc.client.entity.ArcField;
import com.meida.module.arc.client.entity.ArcReport;
import com.meida.module.arc.client.enums.ArchiveEnumInteger;
import com.meida.module.arc.client.enums.CategoryTypeEnum;
import com.meida.module.arc.client.enums.ReportTypeEnum;
import com.meida.module.arc.client.handler.ReportExportHandler;
import com.meida.module.arc.client.utils.ArcUtils;
import com.meida.module.arc.provider.mapper.ArcReportMapper;
import com.meida.module.arc.provider.service.ArcCategoryService;
import com.meida.module.arc.provider.service.ArcFieldService;
import com.meida.module.arc.provider.service.ArcReportService;
import com.meida.module.file.provider.oss.FileUploadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 报表模板接口实现类
 *
 * @author flyme
 * @date 2021-12-26
 */
@Service
@Transactional(rollbackFor = Exception.class)
@DS("sharding")
public class ArcReportServiceImpl extends BaseServiceImpl<ArcReportMapper, ArcReport> implements ArcReportService {

    @Autowired
    private ArcCategoryService arcCategoryService;

    @Autowired
    private ArcFieldService arcFieldService;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public ResultBody beforeAdd(CriteriaSave cs, ArcReport report, EntityMap extra) {
        ApiAssert.isNotEmpty("报表模板配置不能为空",report);
        ApiAssert.isNotEmpty("类型不能为空",report.getReportType());
        ApiAssert.isNotEmpty("名称不能为空",report.getName());
        ApiAssert.isNotEmpty("模板文件不能为空",report.getTempPath());
        ApiAssert.isNotEmpty("是否单条不能为空",report.getIsOne());
        ApiAssert.isNotEmpty("全宗id不能为空",report.getQzId());
        if(FlymeUtils.isEmpty(report.getStatus())){
            report.setStatus(ArchiveEnumInteger.IS_TRUE.getCode());
        }
        CriteriaQuery<ArcReport> criteriaQuery = new CriteriaQuery<ArcReport>(ArcReport.class);
        criteriaQuery.lambda().eq(ArcReport::getQzId,report.getQzId()).orderByDesc(ArcReport::getSeq);
        criteriaQuery.limit(1);
        ArcReport maxSeqObj = this.getOne(criteriaQuery);
        if(FlymeUtils.isNotEmpty(maxSeqObj)){
            report.setSeq(FlymeUtils.isNotEmpty(maxSeqObj.getSeq())?maxSeqObj.getSeq()+1:0);
        }else{
            report.setSeq(0);
        }
        if(ReportTypeEnum.REPORT_TYPE_1.getCode().equals(report.getReportType())
                ||ReportTypeEnum.REPORT_TYPE_4.getCode().equals(report.getReportType())) {
            ApiAssert.isNotEmpty("门类id不能为空" , report.getCategoryId());

            List<ArcCategory> arcCategories = arcCategoryService.listByIds(StrUtil.split(report.getCategoryId(), ','));
            //ArcCategory category = arcCategoryService.getById(report.getCategoryId());
            ApiAssert.isNotEmpty("门类id不能为空" , arcCategories);
            List<ArcCategory> errCategories = arcCategories.stream().filter(c -> CategoryTypeEnum.CATEGORY_TYPE_0.getCode().equals(c.getType())).collect(Collectors.toList());
            //if(CategoryTypeEnum.CATEGORY_TYPE_0.getCode().equals(category.getType())){
            if (CollUtil.isNotEmpty(errCategories)) {
                ApiAssert.failure("文件夹类型门类不能配置报表" );
            }
            //report.setCategoryName(category.getCnName());
            report.setCategoryName(arcCategories.stream().map(ArcCategory::getCnName).collect(Collectors.joining("," )));
        }else{
            //除了类型为档案 其余类型只能添加一条
            CriteriaQuery<ArcReport> checkQuery = new CriteriaQuery<ArcReport>(ArcReport.class);
            checkQuery.lambda().eq(ArcReport::getQzId,report.getQzId())
                    .eq(ArcReport::getReportType,report.getReportType());
            Long count = this.count(checkQuery);
            if(count>0){
                ReportTypeEnum reportTypeEnum = ReportTypeEnum.getValue(report.getReportType());
                ApiAssert.failure(reportTypeEnum==null?"":reportTypeEnum.getName()+"只能有一条配置");
            }
        }
        //1，档案 2，档案利用-催还单 3，档案利用-出库单，4档案销毁-销毁清册
        if(report.getReportType().equals(ReportTypeEnum.REPORT_TYPE_1.getCode())){
            report.setBeanName("arcInfoReportHandler");
        }else if(report.getReportType().equals(ReportTypeEnum.REPORT_TYPE_2.getCode())){
            report.setBeanName("arcUseReportHandler");
        }else if(report.getReportType().equals(ReportTypeEnum.REPORT_TYPE_3.getCode())){
            report.setBeanName("arcUseReportHandler");
        }else if(report.getReportType().equals(ReportTypeEnum.REPORT_TYPE_4.getCode())){
            report.setBeanName("arcInfoReportHandler");
        }

        report.setLocalPath("update");
        return ResultBody.ok();
    }

    synchronized void loadData2Redis() {
        if (redisUtils.hasKey(ArchiveConstants.ARC_REPORT_PREFIX)) {
            redisUtils.del(ArchiveConstants.ARC_REPORT_PREFIX);
        }
        //List<ArcReport> list = this.list( new QueryWrapper<ArcReport>().lambda().orderByAsc(ArcReport::getSeq));
        //redisUtils.setList(ArchiveConstants.ARC_REPORT_PREFIX,list);
    }

    @Override
    public ResultBody afterAdd(CriteriaSave cs, ArcReport arcReport, EntityMap extra) {
        loadData2Redis();
        return ResultBody.ok("保存成功", arcReport);
    }

    @Override
    public ResultBody beforeEdit(CriteriaUpdate<ArcReport> cu, ArcReport t, EntityMap extra) {
        ApiAssert.isNotEmpty("编辑对象不能为空",t);
        ArcReport before = getById(t.getReportId());
        ApiAssert.isNotEmpty("编辑对象不能为空",before);
        if(!before.getTempPath().equals(t.getTempPath())){
            t.setLocalPath("update");
        }
        if(t.getReportType()!=null&&!before.getReportType().equals(t.getReportType())){
            ApiAssert.failure("类型不可编辑");
        }

        /*if(before.getCategoryId()==null){
            if(t.getCategoryId()!=null){
                ApiAssert.failure("门类不可编辑");
            }
        }else{
            if(t.getCategoryId()!=null&&!t.getCategoryId().equals(before.getCategoryId())){
                ApiAssert.failure("门类不可编辑");
            }
        }*/
        if (ReportTypeEnum.REPORT_TYPE_1.getCode().equals(before.getReportType())
                || ReportTypeEnum.REPORT_TYPE_4.getCode().equals(before.getReportType())) {
            List<ArcCategory> arcCategories = arcCategoryService.listByIds(StrUtil.split(t.getCategoryId(), ','));
            ApiAssert.isNotEmpty("门类id不能为空" , arcCategories);
            List<ArcCategory> errCategories = arcCategories.stream().filter(c -> CategoryTypeEnum.CATEGORY_TYPE_0.getCode().equals(c.getType())).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(errCategories)) {
                ApiAssert.failure("文件夹类型门类不能配置报表" );
            }
            t.setCategoryName(arcCategories.stream().map(ArcCategory::getCnName).collect(Collectors.joining("," )));
        }
        return ResultBody.ok();
    }

    @Override
    public ResultBody afterEdit(CriteriaUpdate cu, ArcReport arcReport, EntityMap extra) {
        loadData2Redis();
        return ResultBody.ok("更新成功", arcReport);
    }

    @Override
    public ResultBody beforeListEntityMap(CriteriaQuery<ArcReport> cq, ArcReport report, EntityMap requestMap) {
        ApiAssert.isNotEmpty("全宗id不能为空",report.getQzId());
        cq.eq("qzId");
        ApiAssert.isNotEmpty("报表类型不能为空",report.getReportType());
        cq.eq("reportType");
        cq.eq("status",ArchiveEnumInteger.IS_TRUE.getCode());
        //ApiAssert.isNotEmpty("是否单条不能为空",report.getIsOne());
        cq.eq(ObjectUtil.isNotEmpty(requestMap.get("isOne")), "isOne", requestMap.get("isOne"));
        if(ReportTypeEnum.REPORT_TYPE_1.getCode()
                .equals(report.getReportType())||ReportTypeEnum.REPORT_TYPE_4.getCode().equals(report.getReportType())){
            ApiAssert.isNotEmpty("档案和销毁库类型报表需要传门类id" , report.getCategoryId());
            cq.like("categoryId" , report.getCategoryId());
        }
        cq.orderByAsc("seq");
        return ResultBody.ok();
    }


    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public ResultBody beforePageList(CriteriaQuery<ArcReport> cq, ArcReport report, EntityMap requestMap) {
        ApiAssert.isNotEmpty("全宗id不能为空",report.getQzId());
        cq.eq("qzId");
        cq.eq("seq");
        cq.eq("reportType");
        cq.like(ArcReport.class, "name" );
        cq.like(ArcReport.class, "tempPath" );
        cq.like(ArcReport.class, "categoryName" );
        cq.eq("isOne" );
        cq.eq("status" );
        cq.like(ArcReport.class, "beanName" );
        cq.orderByAsc("report.seq" );
        return ResultBody.ok();
    }

    @Override
    public ResultBody afterDelete(CriteriaDelete cd, Long[] ids) {
        loadData2Redis();

        return super.afterDelete(cd, ids);
    }

    @Override
    public List<EntityMap> afterPageList(CriteriaQuery<ArcReport> cq, List<EntityMap> data, ResultBody resultBody) {
        return data;
    }

    @Override
    public void export(Map params, HttpServletRequest request, HttpServletResponse response) {
        Object reportId = params.get("reportId" );
        ApiAssert.isNotEmpty("报表id不能为空" , reportId);
        ArcReport obj = this.getById(Long.parseLong(reportId.toString()));

        if (FlymeUtils.isEmpty(obj.getLocalPath()) || "update".equals(obj.getLocalPath())) {
            ApiAssert.isNotEmpty("模板路径为空" , obj.getTempPath());
            downLoadAndGenReportLocalPath(obj);
        }else{
            try{
                File f = new File(obj.getLocalPath());
                if(f==null||!f.exists()||!f.isFile()){
                    changeLocalPath(obj.getReportId(),null);
                    downLoadAndGenReportLocalPath(obj);
                }
            }catch (Exception e){
                log.error("验证模板异常",e);
                changeLocalPath(obj.getReportId(),null);
                ApiAssert.failure("验证模板异常");
            }

        }
        ReportExportHandler handler = (ReportExportHandler) SpringContextHolder.getHandler(obj.getBeanName(), ReportExportHandler.class);
        ApiAssert.isNotEmpty("找不到模板数据源处理类",handler);
        String fileType = obj.getTempPath().substring(obj.getTempPath().lastIndexOf("."));
        handler.export(params,request,response,obj.getLocalPath(),obj.getName()+fileType);
    }

    @Override
    public ResultBody upOrdown(Map params) {
        Object reportId = params.get("reportId");
        ApiAssert.isNotEmpty("报表id不能为空",reportId);
        Object qzId = params.get("qzId");
        ApiAssert.isNotEmpty("全宗id不能为空",qzId);
        Object type = params.get("type");
        ApiAssert.isNotEmpty("移动类型不能为空",type);
        int typeInt = Integer.parseInt(type.toString());


        ArcReport report = this.getById(Long.parseLong(reportId.toString()));
        ApiAssert.isNotEmpty("报表不能为空",report);
        CriteriaQuery<ArcReport> query = new CriteriaQuery<ArcReport>(ArcReport.class);
        query.lambda().eq(ArcReport::getQzId,Long.parseLong(qzId.toString()));
        query.orderByAsc("seq");//基于seq从大到小排序
        List<ArcReport> list = this.list(query);
        int sourceIndex = -1;
        int targetIndex = -1;
        for(int i=0;i<list.size();i++){
            ArcReport obj = list.get(i);
            if(obj.getReportId().equals(report.getReportId())){
                if(typeInt==1){//上移
                    if(i==0){
                        return ResultBody.failed("无法上移");
                    }else{
                        sourceIndex = i;
                        targetIndex = i-1;
                    }
                }else if(typeInt==2){//下移
                    if(i==list.size()-1){
                        return ResultBody.failed("无法下移");
                    }else{
                        sourceIndex = i;
                        targetIndex = i+1;
                    }
                }
            }
        }
        if(sourceIndex<0||targetIndex<0){
            return ResultBody.failed("移动错误");
        }
        Long targetId = list.get(sourceIndex).getReportId();
        list.get(sourceIndex).setReportId(list.get(targetIndex).getReportId());
        list.get(targetIndex).setReportId(targetId);
        List<ArcReport> updateObj = new ArrayList<>();
        for(int i=0;i<list.size();i++){
            ArcReport obj = new ArcReport();
            obj.setReportId(list.get(i).getReportId());
            obj.setSeq(i);
            updateObj.add(obj);
        }
        this.saveOrUpdateBatch(updateObj);
        return ResultBody.ok();
    }

    @Override
    public ResultBody getReportFieldByType(Map params) {
        Object reportType = params.get("reportType");

        Object categoryId = params.get("categoryId");
        ApiAssert.isNotEmpty("模板类型不能为空",reportType);
        Integer reportTypeInt = Integer.parseInt(reportType.toString());
        Map<String,String> result = new HashMap<String,String>();
        //1，档案 2，档案利用-催还单 3，档案利用-出库单，4档案销毁-销毁清册
        if(reportTypeInt.equals(ReportTypeEnum.REPORT_TYPE_1.getCode())||reportTypeInt.equals(ReportTypeEnum.REPORT_TYPE_4.getCode())){
            ApiAssert.isNotEmpty("档案类型的模板需选择门类才能查看字段",categoryId);
            CriteriaQuery<ArcField> criteriaQuery = new CriteriaQuery<ArcField>(ArcField.class);
            criteriaQuery.lambda().eq(ArcField::getCategoryId,Long.parseLong(categoryId.toString()));
            List<ArcField> list = arcFieldService.list(criteriaQuery);
            result.put("index","序号");
            result.put("categoryName","门类名称");
            result.put("categoryType","门类类型");
            result.putAll(list.stream().collect(Collectors.toMap(ArcField::getFieldName,ArcField::getFieldCnName)));
        }else if(reportTypeInt.equals(ReportTypeEnum.REPORT_TYPE_2.getCode())){
            result.put("categoryName","借阅门类名称");
            result.put("categoryType","借阅门类类型");
            result.put("archiveName","借阅档案名称");
            result.put("arcNo","借阅档号");
            result.put("useUnitId","借阅人单位");
            result.put("useUserName","借阅人名称");
            result.put("usePhone","借阅人电话");
            result.put("useTime","出借日期");
            result.put("borrowDay","出借天数");
            result.put("isOriginal","是否显示原文");
            result.put("isPrint","是否允许打印");
            result.put("isDownload","是否允许下载原文");
            result.put("canDownloadCount","允许下载次数");
            result.put("downloadCount","已下载次数");

            result.put("nowData","当前日期");
            result.put("nowTime","当前时间");

            result.put("index","序号");
        }else if(reportTypeInt.equals(ReportTypeEnum.REPORT_TYPE_3.getCode())){
            result.put("categoryName","借阅门类名称");
            result.put("categoryType","借阅门类类型");
            result.put("archiveName","借阅档案名称");
            result.put("arcNo","借阅档号");
            result.put("useUnitId","借阅人单位");
            result.put("useUserName","借阅人名称");
            result.put("usePhone","借阅人电话");
            result.put("useTime","出借日期");
            result.put("borrowDay","出借天数");
            result.put("isOriginal","是否显示原文");
            result.put("isPrint","是否允许打印");
            result.put("isDownload","是否允许下载原文");
            result.put("canDownloadCount","允许下载次数");
            result.put("downloadCount","已下载次数");

            result.put("nowData","当前日期");
            result.put("nowTime","当前时间");

            result.put("index","序号");
        }else{
            return ResultBody.failed("模板类型错误");
        }
        return ResultBody.ok(result);
    }

    private void downLoadAndGenReportLocalPath(ArcReport obj){
        try{
            if(obj.getTempPath().indexOf("http")>-1){
                //网络下载
                int byteread = 0;
                URL url = new URL(obj.getTempPath());
                URLConnection conn = url.openConnection();
                InputStream inputStream = conn.getInputStream();
                String fileBasePath = FileUploadUtil.datePath() + "/";
                String rootPath = ArcUtils.getRootTempPath();
                String fileName = obj.getTempPath().substring(obj.getTempPath().lastIndexOf("/"));
                String localPath = rootPath+fileBasePath;
                File f = createFile(localPath, fileName);
                FileOutputStream fs = new FileOutputStream(f);
                byte[] buffer = new byte[1024];
                while((byteread= inputStream.read(buffer))!=-1){
                    fs.write(buffer,0,byteread);
                }
                fs.flush();
                fs.close();
                inputStream.close();
                obj.setLocalPath(localPath+fileName);
                changeLocalPath(obj.getReportId(),localPath+fileName);
            }else{
                //本地迁移
                obj.setLocalPath(obj.getTempPath());
                changeLocalPath(obj.getReportId(),obj.getTempPath());
            }
        }catch(Exception e){
            log.error("生成本地模板异常",e);
            ApiAssert.failure("生成本地模板异常");
        }
    }

    private void changeLocalPath(Long reportId,String value){
        LambdaUpdateWrapper<ArcReport> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ArcReport::getReportId,reportId);
        updateWrapper.set(ArcReport::getLocalPath,FlymeUtils.isEmpty(value)?"update":value);
        this.update(updateWrapper);
    }

    private File createFile(String path, String fileName) {
        File dir = new File(path);
        if (!dir.exists()) {
            //创建文件夹
            dir.mkdirs();
        }
        File f = new File(path + "/" + fileName);
        return f;

    }

    @Override
    public ResultBody list4Redis(Map params) {
        List<ArcReport> list = redisUtils.getList(ArchiveConstants.ARC_REPORT_PREFIX);
        if (CollUtil.isEmpty(list)) {
            loadData2Redis();
            list = redisUtils.getList(ArchiveConstants.ARC_REPORT_PREFIX);
        }
        ArcReport report = BeanUtil.toBean(params, ArcReport.class);
        ApiAssert.isNotEmpty("全宗id不能为空", report.getQzId());

        ApiAssert.isNotEmpty("报表类型不能为空", report.getReportType());
        ApiAssert.isNotEmpty("是否单条不能为空", report.getIsOne());
        List<ArcReport> collect = list.stream().filter(a -> a.getQzId().equals(report.getQzId()) && a.getReportType().equals(report.getReportType()) && a.getStatus().equals(ArchiveEnumInteger.IS_TRUE.getCode())
                && a.getIsOne().equals(report.getIsOne())
        ).collect(Collectors.toList());

        if (ReportTypeEnum.REPORT_TYPE_1.getCode()
                .equals(report.getReportType()) || ReportTypeEnum.REPORT_TYPE_4.getCode().equals(report.getReportType())) {
            ApiAssert.isNotEmpty("档案和销毁库类型报表需要传门类id", report.getCategoryId());
            List<ArcReport> collect1 = collect.stream().filter(a -> a.getCategoryId().contains(report.getCategoryId())).collect(Collectors.toList());
            return ResultBody.ok(collect1);

        }
        return ResultBody.ok(collect);
    }
}
